/*
 * SHOPTNT 版权所有。
 * 未经许可，您不得使用此文件。
 * 官方地址：www.shoptnt.cn
 */
package cn.shoptnt.service.trade.order.impl;

import cn.shoptnt.client.goods.GoodsClient;
import cn.shoptnt.client.goods.GoodsQuantityClient;
import cn.shoptnt.framework.cache.Cache;
import cn.shoptnt.model.trade.order.vo.CommandResult;
import cn.shoptnt.service.trade.order.command.OrderCreateCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.shoptnt.framework.util.StringUtil;
import cn.shoptnt.mapper.trade.order.OrderItemsMapper;
import cn.shoptnt.mapper.trade.order.OrderMapper;
import cn.shoptnt.mapper.trade.order.TradeMapper;
import cn.shoptnt.model.base.CachePrefix;
import cn.shoptnt.model.base.message.OrderStatusChangeMsg;
import cn.shoptnt.model.base.rabbitmq.AmqpExchange;
import cn.shoptnt.model.errorcode.TradeErrorCode;
import cn.shoptnt.model.goods.enums.QuantityType;
import cn.shoptnt.model.goods.vo.GoodsQuantityVO;
import cn.shoptnt.model.goods.vo.GoodsSkuVO;
import cn.shoptnt.model.promotion.tool.enums.PromotionTypeEnum;
import cn.shoptnt.model.trade.cart.vo.CartPromotionVo;
import cn.shoptnt.model.trade.order.dos.*;
import cn.shoptnt.model.trade.order.dto.OrderDTO;
import cn.shoptnt.model.trade.order.enums.OrderStatusEnum;
import cn.shoptnt.model.trade.order.enums.PayStatusEnum;
import cn.shoptnt.model.trade.order.enums.PaymentTypeEnum;
import cn.shoptnt.model.trade.order.enums.TradeStatusEnum;
import cn.shoptnt.model.trade.order.vo.OrderSkuVO;
import cn.shoptnt.model.trade.order.vo.TradeVO;
import cn.shoptnt.service.trade.order.TradeIntodbManager;
import cn.shoptnt.framework.exception.ServiceException;
import cn.shoptnt.framework.rabbitmq.MessageSender;
import cn.shoptnt.framework.rabbitmq.MqMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @description: 交易入库业务类
 * @author: liuyulei
 * @create: 2020/3/20 15:29
 * @version:1.0
 * @since: 7.2.0
 **/
@Service
public class TradeIntodbManagerImpl implements TradeIntodbManager {

    private Integer orderCacheTimeout = 60 * 60;

    @Autowired
    private TradeMapper tradeMapper;

    @Autowired
    private Cache cache;

    @Autowired
    private MessageSender messageSender;


    @Autowired
    private List<OrderCreateCommand> orderCreateCommands;

    /**
     * 入库处理
     *
     * @param tradeVO 交易VO
     */
    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED,
            rollbackFor = {RuntimeException.class, ServiceException.class, Exception.class})
    public void intoDB(TradeVO tradeVO) {
        if (tradeVO == null) {
            throw new RuntimeException("交易无法入库，原因：trade为空");
        }
        //交易单状态
        String tradeStatus = OrderStatusEnum.CONFIRM.value();
        // 交易入库
        TradeDO tradeDO = new TradeDO(tradeVO);

        tradeDO.setTradeStatus(tradeStatus);
        tradeMapper.insert(tradeDO);

        //为交易创建订单信息 失败后可进行回滚
        this.crateOrders(tradeVO);

        //将交易VO放入缓存，失效时间为1小时
        String cacheKey = getTradeCacheKey(tradeVO.getTradeSn());
        this.cache.put(cacheKey, tradeVO, orderCacheTimeout);
        //发送订单创建消息
        this.messageSender.send(new MqMessage(AmqpExchange.ORDER_CREATE,
                AmqpExchange.ORDER_CREATE + "_ROUTING",
                cacheKey));

    }


    /**
     * 为交易创建订单信息
     * 根据交易对象创建订单,
     * 在创建过程中,当某个订单出现失败时回滚之前创建成功的信息
     * 通过创建订单返回的成功信息,可以对成功的数据进行回滚
     * 回滚内容包括 订单DB入库信息 商品库存信息  会员积分扣减 会员优惠券使用
     * 当出现错误情况时 抛出自定义的业务异常
     * @param tradeVO 交易对象
     */
    private void crateOrders(TradeVO tradeVO) {
        List<CommandResult> rollbackList = new ArrayList();
        //获取交易单中的订单信息集合
        List<OrderDTO> orderList = tradeVO.getOrderList();
        for (OrderDTO orderDTO : orderList) {
            //使用命令者模式 对订单创建与扣减
            CommandResult result = this.crateOrder(orderDTO, rollbackList);
            //如果创建失败则抛出异常
            if (!result.getResult()) {
                //失败回滚已经执行过的操作
                rollbackList.forEach(CommandResult::rollback);
                throw new ServiceException(TradeErrorCode.E456.name(), result.getErrorMessage());
            }
        }
    }



    /**
     * 命令者模式 创建订单
     * 创建订单过程中 可以进行rollback的步骤如下:
     * 1.订单DB入库信息
     * 2.扣减商品库存
     * 3.积分商品下单,扣减会员积分
     * 4.订单优惠券使用
     * @param orderDTO  订单对象
     * @param resultList  返回结果集合
     * @return
     */
    private CommandResult crateOrder(OrderDTO orderDTO, List<CommandResult> resultList) {

        for (OrderCreateCommand command : orderCreateCommands) {
            //执行命令 记录每次成功的命令 当出现失败情况时 立即停止
            CommandResult result = command.execute(orderDTO);
            //判断如果存在失败的命令立即停止执行
            if (!result.getResult()) {
                return result;
            } else {
                //保存执行成功的命令
                resultList.add(result);
            }
        }
        //默认返回成功状态
        return new CommandResult(true, "成功!");


    }


    /**
     * 获取交易缓存key
     *
     * @param tradeSn
     * @return
     */
    public String getTradeCacheKey(String tradeSn) {
        //重新压入缓存
        String cacheKey = CachePrefix.TRADE_SESSION_ID_PREFIX.getPrefix() + tradeSn;
        return cacheKey;
    }


}
